Skip to content

Fix never-type rvalue ICE #47746

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 28, 2018
Merged

Fix never-type rvalue ICE #47746

merged 2 commits into from
Jan 28, 2018

Conversation

varkor
Copy link
Member

@varkor varkor commented Jan 25, 2018

This fixes #43061.
r? @nikomatsakis

A small post-mortem as a follow-up to our investigations in #47291:
The problem as I understand it is that when NeverToAny coercions are made, the expression/statement that is coerced may be enclosed in a block. In our case, the statement x; was being transformed to something like: NeverToAny( {x;} ). Then, NeverToAny is transformed into an expression:

ExprKind::NeverToAny { source } => {
let source = this.hir.mirror(source);
let is_call = match source.kind {
ExprKind::Call { .. } => true,
_ => false,
};
unpack!(block = this.as_local_rvalue(block, source));

Which ends up calling ast_block_stmts on the block {x;}, which triggers this condition:
// Then, the block may have an optional trailing expression which is a “return” value
// of the block.
if let Some(expr) = expr {
unpack!(block = this.into(destination, block, expr));
} else {
this.cfg.push_assign_unit(block, source_info, destination);
}

In our case, there is no return expression, so push_assign_unit is called. But the block has already been recorded as diverging, meaning the result of the block will be assigned to a location of type !, rather than (). This causes the MIR error.
I'm assuming the NeverToAny coercion code is doing what it's supposed to (there don't seem to be any other problems), so fixing the issue simply consists of checking that the destination for the return value actually is supposed to be a unit. (If no return value is given, the only other possible type for the return value is !, which can just be ignored, as it will be unreachable anyway.)

I checked the other cases of push_assign_unit, and it didn't look like they could be affected by the divergence issue (blocks are kind of special-cased in this regard as far as I can tell), so this should be sufficient to fix the issue.

this.cfg.push_assign_unit(block, source_info, destination);
let tcx = this.hir.tcx();
let ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
if ty.is_nil() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be sure I understand, the premise here is that the type is either () or else the block must diverge, since any other type would have yielded a compilation error?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, with the current semantics, any other type should be impossible (i.e. any other type must be explicit and go through the Some(expr) branch instead) , because the only implicit return types are () and !.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a comment? That's a bit non-obvious. (If the code were like if ty.is_never() it'd be more obvious...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. (The reason I opted for ty.is_nil() rather than ty.is_never() here is because it seemed less contextual — it's always true that we only want to assign a unit if the type is unit. One alternative to make it absolutely explicit would be to check for !ty.is_never() in an else clause and throw a warning there that it should be impossible, but this seems like overkill.)

@kennytm kennytm added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 25, 2018
@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Collaborator

bors commented Jan 26, 2018

📌 Commit adcb37e has been approved by nikomatsakis

@nikomatsakis nikomatsakis added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 26, 2018
@bors
Copy link
Collaborator

bors commented Jan 27, 2018

⌛ Testing commit adcb37e with merge 9f95bf37e2e7cc237a6c4ee38cdb24a5dda56a52...

@bors
Copy link
Collaborator

bors commented Jan 27, 2018

💔 Test failed - status-appveyor

@kennytm
Copy link
Member

kennytm commented Jan 27, 2018

@bors retry #46903

@bors
Copy link
Collaborator

bors commented Jan 28, 2018

⌛ Testing commit adcb37e with merge 6beb06e...

bors added a commit that referenced this pull request Jan 28, 2018
Fix never-type rvalue ICE

This fixes #43061.
r? @nikomatsakis

A small post-mortem as a follow-up to our investigations in #47291:
The problem as I understand it is that when `NeverToAny` coercions are made, the expression/statement that is coerced may be enclosed in a block. In our case, the statement `x;` was being transformed to something like: `NeverToAny( {x;} )`. Then, `NeverToAny` is transformed into an expression:
https://github.com/rust-lang/rust/blob/000fbbc9b8f88adc6a417f1caef41161f104250f/src/librustc_mir/build/expr/into.rs#L52-L59
Which ends up calling `ast_block_stmts` on the block `{x;}`, which triggers this condition:
https://github.com/rust-lang/rust/blob/000fbbc9b8f88adc6a417f1caef41161f104250f/src/librustc_mir/build/block.rs#L141-L147
In our case, there is no return expression, so `push_assign_unit` is called. But the block has already been recorded as _diverging_, meaning the result of the block will be assigned to a location of type `!`, rather than `()`. This causes the MIR error.
I'm assuming the `NeverToAny` coercion code is doing what it's supposed to (there don't seem to be any other problems), so fixing the issue simply consists of checking that the destination for the return value actually _is_ supposed to be a unit. (If no return value is given, the only other possible type for the return value is `!`, which can just be ignored, as it will be unreachable anyway.)

I checked the other cases of `push_assign_unit`, and it didn't look like they could be affected by the divergence issue (blocks are kind of special-cased in this regard as far as I can tell), so this should be sufficient to fix the issue.
@bors
Copy link
Collaborator

bors commented Jan 28, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 6beb06e to master...

@bors bors merged commit adcb37e into rust-lang:master Jan 28, 2018
@varkor varkor deleted the never-type-ice branch April 25, 2018 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rust 1.20.0-nightly ICE with never_type: Broken MIR
4 participants